package drds.global_transaction.resource_manager.execute.dml;

import java.sql.SQLException;
import java.sql.Statement;

import drds.global_transaction.resource_manager.execute.AbstractExecutor;
import drds.global_transaction.resource_manager.execute.LockConflictException;
import drds.global_transaction.resource_manager.execute.LockRetryController;
import drds.global_transaction.resource_manager.execute.StatementExecuteTemplate;
import drds.global_transaction.resource_manager.model.Image;
import drds.global_transaction.resource_manager.proxy.resource_manager.ConnectionProxy;
import drds.global_transaction.resource_manager.proxy.resource_manager.StatementProxy;
import drds.global_transaction.resource_manager.sql_recognizer.SqlRecognizer;

public abstract class AbstractDmlExecutor<ResultObject, S extends Statement> extends AbstractExecutor<ResultObject, S>
{

	public AbstractDmlExecutor(SqlRecognizer sqlRecognizer, StatementProxy<S> statementProxy, StatementExecuteTemplate<ResultObject, S> statementExecuteTemplate)
	{
		super(sqlRecognizer, statementProxy, statementExecuteTemplate);
	}

	@Override
	public ResultObject doExecute(Object... args) throws Exception
	{
		ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
		if (connectionProxy.getAutoCommit())
		{
			return executeByAutoCommitWay(args);
		} else
		{
			return executeNonAutoCommitWay(args);
		}
	}

	protected ResultObject executeByAutoCommitWay(Object[] args) throws Exception
	{
		ResultObject result = null;
		ConnectionProxy connectionProxy = statementProxy.getConnectionProxy();
		LockRetryController lockRetryController = new LockRetryController();
		try
		{
			connectionProxy.setAutoCommit(false);
			while (true)
			{
				try
				{
					result = executeNonAutoCommitWay(args);
					connectionProxy.commit();
					break;
				} catch (LockConflictException lockConflict)
				{
					lockRetryController.sleep(lockConflict);
				}
			}

		} finally
		{
			connectionProxy.setAutoCommit(true);

		}
		return result;
	}

	protected ResultObject executeNonAutoCommitWay(Object[] args) throws Exception
	{
		// 前置image
		Image beforeImage = beforeImage();
		ResultObject resultObject = statementExecuteTemplate.execute(statementProxy.getStatement(), args);
		// 后置image
		Image afterImage = afterImage(beforeImage);
		statementProxy.getConnectionProxy().prepareUndoLog(sqlRecognizer.getSqlType(), sqlRecognizer.getTableName(), beforeImage, afterImage);
		return resultObject;
	}

	//
	protected abstract Image beforeImage() throws SQLException;

	protected abstract Image afterImage(Image beforeImage) throws SQLException;

}
